<?php

namespace app\teacher\library;

use app\common\enums\CourseFullTypeEnum;
use app\common\enums\OrganizationStateEnum;
use app\common\enums\TrainerStatusEnum;
use app\common\facade\Token;
use app\common\model\banxi\learn\LearnSkRecord;
use app\common\model\banxi\learn\LearnSkWordRecord;
use app\common\model\banxi\organization\Organization;
use app\common\model\banxi\trainer\Trainer;
use ba\Random;
use think\facade\Config;
use Throwable;

/**
 * 公共权限类（会员权限类）
 * @property int $id         会员ID
 * @property string $username   会员用户名
 * @property string $nickname   会员昵称
 * @property string $email      会员邮箱
 * @property string $mobile     会员手机号
 * @property string $password   密码密文
 * @property string $salt       密码盐
 */
class Auth extends \app\common\library\Auth
{
    /**
     * 需要登录时/无需登录时的响应状态代码
     */
    public const LOGIN_RESPONSE_CODE = 401;


    /**
     * 需要权限时/无需权限时的响应状态代码
     */
    public const PERMISSION_RESPONSE_CODE = 409;


    /**
     * 需要登录标记 - 前台应清理 token、记录当前路由 path、跳转到登录页
     */
    public const NEED_LOGIN = 'need login';

    /**
     * 已经登录标记 - 前台应跳转到基础路由
     */
    public const LOGGED_IN = 'logged in';

    /**
     * 是否登录
     * @var bool
     */
    protected bool $loginEd = false;

    /**
     * 错误消息
     * @var string
     */
    protected string $error = '';

    /**
     * Model实例
     * @var
     */
    protected $model = null;

    /**
     * 令牌
     * @var string
     */
    protected string $token = '';

    /**
     * 刷新令牌
     * @var string
     */
    protected string $refreshToken = '';

    /**
     * 令牌默认有效期
     * 可在 config/buildadmin.php 内修改默认值
     * @var int
     */
    protected int $keepTime = 86400;

    /**
     * 刷新令牌有效期
     * @var int
     */
    protected int $refreshTokenKeepTime = 2592000;
    public const TOKEN_TYPE = 'trainer'; //老师
    protected $organizationId = 0;
    /**
     * 允许输出的字段
     * @var array
     */
    protected array $allowFields = [
        'createBy', 'createTime', 'updateBy', 'updateTime', 'id', 'del', 'account', 'password', 'salt', 'avatar',
        'idCardFront', 'idCardBack', 'name', 'organizationId', 'title', 'phone', 'birthday', 'age', 'bankCard', 'bankName', 'bankUser', 'recommend',
        'recommendCode', 'level', 'levelCn', 'sex', 'check', 'checkCn', 'practice', 'practiceCn', 'isPublic', 'status', 'statusCn', 'lastLoginTime',
        'lastLoginIp'];

    public function __construct(array $config = [])
    {
        parent::__construct(array_merge([
        ], $config));
        $this->setKeepTime((int)Config::get('buildadmin.teacher_token_keep_time'));
    }

    /**
     * 根据Token初始化会员登录态
     * @param $token
     * @return bool
     * @throws Throwable
     */
    public function init($token): bool
    {
        $tokenData = Token::get($token);
        if ($tokenData) {
            /**
             * 过期检查，过期则抛出 @see TokenExpirationException
             */
            Token::tokenExpirationCheck($tokenData);
            $userId = intval($tokenData['user_id']);
            //老师
            if ($tokenData['type'] == self::TOKEN_TYPE && $userId > 0) {
                $this->model = Trainer::where('id', $userId)->find();
                if (!$this->model) {
                    $this->setError('Account not exist');
                    return false;
                }
                if ($this->model->status != TrainerStatusEnum::E3) {
                    $this->setError('Account disabled');
                    return false;
                }
                $this->token = $token;
                $this->loginSuccessful();
                return true;
            }
        }
        $this->reset();
        $this->setError('Token login failed');
        return false;
    }

    /**
     * 老师密码登录
     */
    public function loginPwd(string $account, string $password): array|bool
    {
        $this->model = Trainer::getByAccountOrPhone($account);
        if (!$this->model) {
            $this->setError('Account not exist');
            return false;
        }

        if (!verifyMd5Password($password, $this->model->password, $this->model->salt)) {
            $this->setError('Password is incorrect');
            return false;
        }

        // 清理 token
        $this->clearToken();
        if (!$this->loginSuccessful()) {
            return false;
        }

        return [
            'token' => $this->token,
            'isLogin' => $this->loginEd
        ];
    }

    /**
     * 忘记密码
     * @return bool
     */
    public function forgotPassword($newPassword): bool
    {
        $salt = createPasswordSalt();
        $this->model->salt = $salt;
        $this->model->password = createPassword($salt, $newPassword);
        $this->model->save();
        return true;
    }

    public function getUserInfo(): array
    {
        if (!$this->model) return [];
        $info = $this->model->toArray();
        $info = array_intersect_key($info, array_flip($this->getAllowFields()));

        $Organization = new Organization();
        $info['organizationBaseInfo'] = $Organization->getOrganizationBaseInfo($info['organizationId']);

        //教学次数
        $LearnSkRecordModel = new LearnSkRecord();
        $info['minute30Num'] = $LearnSkRecordModel->where(['trainerId' => $info['id'], 'isEnd' => 1, 'courseFullType' => CourseFullTypeEnum::C_30])->count();
        $info['minute60Num'] = $LearnSkRecordModel->where(['trainerId' => $info['id'], 'isEnd' => 1, 'courseFullType' => CourseFullTypeEnum::C_60])->count();
        //累计教词
        $LearnSkWordRecordModel = new LearnSkWordRecord();
        $info['totalTeachWordNum'] = $LearnSkWordRecordModel->getTotalWordByTrainerId($info['id']);

        return $info;
    }

    /**
     * 清理Token
     * @return bool
     */
    public function clearToken(): bool
    {
        // 清理 token
        if (Config::get('buildadmin.client_sso')) {
            Token::clear(self::TOKEN_TYPE, $this->model->id);
            Token::clear(self::TOKEN_TYPE . '-refresh', $this->model->id);
        }
        return true;
    }

    public function reUpdatePwd($rePwd, $newPassword): bool
    {
        if (!verifyMd5Password($rePwd, $this->model->password, $this->model->salt)) {
            $this->setError('Old password is incorrect');
            return false;
        }
        if (verifyMd5Password($newPassword, $this->model->password, $this->model->salt)) {
            $this->setError('New password cannot be the same as the original one');
            return false;
        }

        $this->forgotPassword($newPassword);
        $this->logout();
        return true;
    }

    public function loginSuccessful(): bool
    {
        if (!$this->model) {
            return false;
        }
        if ($this->model->organizationId == null || $this->model->organizationId == "") {
            $this->setError('账号未绑定机构');
            return false;
        }
        if ($this->model->organizationId > 0) {
            $organization = Organization::where('id', $this->model->organizationId)->find();
            if (!$organization) {
                $this->setError('机构不存在');
                return false;
            }
            if ($organization->status == OrganizationStateEnum::E3) {
                $this->setError('机构被禁用');
                return false;
            }
        }
        try {
            $this->loginEd = true;
            $this->organizationId = $this->model->organizationId;
            if (!$this->token) {
                $this->setToken();
            }
        } catch (Throwable $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 设置Token
     * @return bool
     */
    public function setToken(): bool
    {
        $this->token = Random::uuid();
        Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
        return true;
    }

    /**
     * 退出登录
     * @return bool
     */
    public function logout(): bool
    {
        if (!$this->loginEd) {
            $this->setError('You are not logged in');
            return false;
        }
        return $this->reset();
    }

    /**
     * 属性重置（注销、登录失败、重新初始化等将单例数据销毁）
     */
    protected function reset(bool $deleteToken = true): bool
    {
        if ($deleteToken && $this->token) {
            Token::delete($this->token);
        }
        $this->token = '';
        $this->loginEd = false;
        $this->model = null;
        $this->refreshToken = '';
        $this->setError('');
        $this->setKeepTime((int)Config::get('buildadmin.teacher_token_keep_time'));
        return true;
    }
}